#include "explosion.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <cassert>
using namespace vfx;

static  const COLOR	c0 = ColorRGBA(255, 255, 255, 000);
Explosion::Explosion(int Count, const std::string& Image, bool additive, float Timeout) :
	m_NodeA(NULL),
	m_pImage(NULL),
	m_Count(Count),
	m_ActiveC(0),
	m_Additive(additive),
	m_Timeout(Timeout)
{
	m_pImage = GetGraphics()->LoadImageFromFile(Image);
	if (Count > 0)
	{
		m_NodeA = new Node[Count];
		for (int i=0; i<m_Count; i++)
		{
			m_NodeA[i].Free = true;
		}
	}

}
Explosion::~Explosion()
{
	UTIL_SAFE_DELETEA(m_NodeA);
}

void	Explosion::Render(const math::Vec2& Pos, float Scale, float Radius, float Progress)
{
	if (!m_pImage) return;
	Graphics* pG = GetGraphics();
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();
	SetupRenderState(pG);

	int	Color = (int)((1.0f-Progress)*255);
	UTIL_CLAMP(Color, 10, 255);
	COLOR	c1 = ColorRGBA(Color, Color, Color, Color);
	int Index = GraphicsUtil::FillCircle(Pos* Scale, Radius* Scale, c1, c0, &VertA[0]);
	pG->SetTexture(0, m_pImage);
	pG->DrawVertex2D(VertA, Index);

	RestoreRenderState(pG);
}

void	Explosion::Render(const math::Vec2& Offset, float Scale)
{
	if (!m_pImage) return;
	if (m_Count <= 0) return;
	if (m_ActiveC<=0) return;
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();

	float	Stamp = TIMER().Time();

	Graphics* pG = GetGraphics();
	SetupRenderState(pG);
	int	Index = 0;
	for (int i=0; i<m_Count; i++)
	{
		Node*	pExplosion = &m_NodeA[i];
		if (!pExplosion->Free)
		{
			float	Progress = (Stamp - pExplosion->Time) / m_Timeout;
			int	Color = (int)((1.0f-Progress)*255);
			UTIL_CLAMP(Color, 10, 255);
			COLOR	c1 = ColorRGBA(Color, Color, Color, Color);
			Index+=GraphicsUtil::FillCircle((pExplosion->Pos-Offset) * Scale, pExplosion->Radius* Scale, c1, c0, &VertA[Index]);

			if (Index+(16*3) >= Graphics::MAX_TEMP_VERT) 
			{
				pG->SetTexture(0, m_pImage);
				pG->DrawVertex2D(VertA, Index);
				Index = 0;
			}

			if (Stamp - pExplosion->Time > m_Timeout)
			{
				pExplosion->Free = true;
				m_ActiveC--;
			}
		}
	}
	if (Index > 0 ) 
	{
		pG->SetTexture(0, m_pImage);
		pG->DrawVertex2D(VertA, Index);
	}

	RestoreRenderState(pG);
}
void	Explosion::AddExplosion(const math::Vec2& Pos, float Radius)
{
	if (m_ActiveC >= m_Count)  return;
	for (int i=0; i<m_Count; i++)
	{
		if (m_NodeA[i].Free)
		{
			m_NodeA[i].Pos = Pos;
			m_NodeA[i].Radius = Radius;
			m_NodeA[i].Free = false;
			m_NodeA[i].Time = TIMER().Time();
			m_ActiveC++;
			return;
		}
	}
}

void	Explosion::SetupRenderState(Graphics* pG)
{
	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(true);
    } 

}

void	Explosion::RestoreRenderState(Graphics* pG)
{
	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(false);
    }
}



ExplosionSet::ExplosionSet(EffectMan*	pParent, const std::string& Image, int Count, const math::Vec4& From, const math::Vec4& To) : 
	SFXSet(pParent),
	m_ExpUsed(0),
	m_ExplosionA(0)
{
	Load(Image, Count, From, To);
}

ExplosionSet::~ExplosionSet()
{
	UTIL_SAFE_DELETEA(m_ExplosionA);

}
void	ExplosionSet::Render(const math::Vec2& Offset, float Scale)
{
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();

	if (m_ExplosionA && m_ExpUsed > 0)
	{
		int	Index = 0;
		Graphics* pG = GetGraphics();
		GetGraphics()->SetupAdditiveAlpha(true);

		const COLOR	c0 = ColorRGBA(255, 255, 255, 000);
		for (int i=0; i<m_ExpC; i++)
		{
			if (!m_ExplosionA[i].Time.IsReady())
			{
				float	Fraction = m_ExplosionA[i].Time.GetProgress();
				float	Radius = m_ExplosionA[i].FromRad + ((m_ExplosionA[i].ToRad-m_ExplosionA[i].FromRad)*Fraction);
				int	Color = int( (1.0f-Fraction)*255 );
				UTIL_CLAMP(Color, 10, 255);
				COLOR	c1 = ColorRGBA(Color, Color, Color, Color);
				Index+=GraphicsUtil::FillCircle((m_ExplosionA[i].Pos-Offset)*Scale, Radius*Scale, c1, c0, &VertA[Index]);
				if (Index+(16*3) >= Graphics::MAX_TEMP_VERT) 
				{
					pG->SetTexture(0, m_Sprite.GetImage());
					pG->DrawVertex2D(VertA, Index);
					Index = 0;
				}
			}
		}
		if (Index > 0 ) 
		{
			pG->SetTexture(0, m_Sprite.GetImage());
			pG->DrawVertex2D(VertA, Index);
		}
		GetGraphics()->SetupAdditiveAlpha(false);
	}
}
void	ExplosionSet::Tick(float dt)
{
	if (m_ExplosionA && m_ExpUsed > 0)
	{
		for (int i=0; i<m_ExpC; i++)
		{
			if (!m_ExplosionA[i].Time.IsReady())
			{
				m_ExplosionA[i].Time.Tick(dt);
				if (m_ExplosionA[i].Time.IsReady())
				{
					m_ExpUsed--;
				}
			}
		}
	}
}
void	ExplosionSet::Load(const std::string& Image, int Count, const math::Vec4& From, const math::Vec4& To)
{
	m_Sprite.Create(Image);
	m_ExplosionA = new Explosion[Count];
	for (int i=0; i<Count; i++)
	{
		m_ExplosionA[i].Time.SetLimit(1);
		m_ExplosionA[i].Time.ForceReady();
	}
	m_From = From;
	m_To = To;
	m_ExpUsed = 0;
	m_ExpC = Count;
}

void	ExplosionSet::Trigger(const math::Vec2& Pos, float Time, float FromR, float ToR)
{
	if (m_ExpUsed == m_ExpC) return;
	for (int i=0; i<m_ExpC; i++)
	{
		if (m_ExplosionA[i].Time.IsReady())
		{
			m_ExplosionA[i].Time.SetLimit(Time);
			m_ExplosionA[i].Pos = Pos;
			m_ExplosionA[i].FromRad = FromR;
			m_ExplosionA[i].ToRad = ToR;
			m_ExpUsed++;
			return;
		}
	}
}

